/* interrupt.h */
#ifndef _LINUX_INTERRUPT_H
#define _LINUX_INTERRUPT_H

#include <linux/linkage.h>
#include <asm/bitops.h>

/* 
	网络代码部分在数据报接收时需要用到此文件中定义的“下半部分（Bottom Half）“结构及相关函数。
	“下半部分”是一种软件处理手段。为减少中断处理程序的执行时间，一般将中断处理程序人为的分为两个部分：
	“上半部分”和“下半部分”。“上半部分”主要完成一些比较紧急的工作，在完成这些工作后，
	其安排“下半部分”完成剩下一般性工作，虽然这些工作也同样重要，但并不要求实时，故可稍后完成。
	这样就大大减少了实际中断处理程序的执行时间，中断处理程序在处理完“上半部分”后即可返回。从而从整体上提高了系统性能。
*/

struct bh_struct {
	void (*routine)(void *);	// 执行函数
	void *data;					// 参数
};

// bh_active表示正处于活跃状态的“下半部分”，当一个“下半部分”处于活跃状态时，即表示该“下半部分”需要执行。
extern unsigned long bh_active;
// bh_mask表示掩码，即是我们通常所说的使能位，这是 32 位比特的整型数值，每位对应一个“下半部分”。当某位为 0 时，
// 表示屏蔽该“下半部分”，此时即使该“下半部分”处于活跃状态，也不会被系统执行。
extern unsigned long bh_mask;
// bh_base 是 bh_struct 结构类型的数组，共有 32 个元素，每个元素对应一个系统“下半部分”。系统目前使用了前 8
// 个元素，如文件中 enum 定义。其中用于网络代码部分的“下半部分”是第五个位置所指向
// 的元素即 bh_base[INET_BH]。而系统中（包括网络部分代码）所使用的大量定时器则是作
// 为第一个“下半部分”元素执行的。
extern struct bh_struct bh_base[32];

asmlinkage void do_bottom_half(void);

/* Who gets which entry in bh_base.  Things which will occur most often
   should come first - in which case NET should be up the top with SERIAL/TQUEUE! */
   
enum {
	TIMER_BH = 0,
	CONSOLE_BH,
	TQUEUE_BH,
	SERIAL_BH,
	NET_BH,
	IMMEDIATE_BH,
	KEYBOARD_BH,
	CYCLADES_BH
};

// 改变某个“下半部分”的活跃状态。
extern inline void mark_bh(int nr)
{
	set_bit(nr, &bh_active);
}
// enable_bh, disable_bh 这两个函数用于改变“下半部分”的使能位。
extern inline void disable_bh(int nr)
{
	clear_bit(nr, &bh_mask);
}

extern inline void enable_bh(int nr)
{
	set_bit(nr, &bh_mask);
}

extern inline void start_bh_atomic(void)
{
	intr_count++;
}

extern inline void end_bh_atomic(void)
{
	if (intr_count == 1 && (bh_active & bh_mask))
		do_bottom_half();
	intr_count--;
}

/*
 * Autoprobing for irqs:
 *
 * probe_irq_on() and probe_irq_off() provide robust primitives
 * for accurate IRQ probing during kernel initialization.  They are
 * reasonably simple to use, are not "fooled" by spurious interrupts,
 * and, unlike other attempts at IRQ probing, they do not get hung on
 * stuck interrupts (such as unused PS2 mouse interfaces on ASUS boards).
 *
 * For reasonably foolproof probing, use them as follows:
 *
 * 1. clear and/or mask the device's internal interrupt.
 * 2. sti();
 * 3. irqs = probe_irq_on();      // "take over" all unassigned idle IRQs
 * 4. enable the device and cause it to trigger an interrupt.
 * 5. wait for the device to interrupt, using non-intrusive polling or a delay.
 * 6. irq = probe_irq_off(irqs);  // get IRQ number, 0=none, negative=multiple
 * 7. service the device to clear its pending interrupt.
 * 8. loop again if paranoia is required.
 *
 * probe_irq_on() returns a mask of snarfed irq's.
 *
 * probe_irq_off() takes the mask as a parameter,
 * and returns the irq number which occurred,
 * or zero if none occurred, or a negative irq number
 * if more than one irq occurred.
 */

/*
	探测中断号，用法如下：
	1．清除设备的先前中断标志
	2．调用 sti 函数使能系统中断
	3．调用 probe_irq_on 函数，保存其返回的所有空闲中断号
	4．设置设备使其产生一个中断
	5．等待一段合理的时间，等待系统探测到该中断
	6．调用 probe_irq_off 函数，并将之前由 probe_irq_on 返回的空闲中断号作为参数传替给
	probe_irq_off 函数。
	7．检查 probe_irq_off 函数的返回值：
	1>0—没有探测该设备的中断号
	对于此种情况可以在执行一次这个流程，并适当延长第 5 步中等待时间。
	2>负数—检测到多个中断号：根据具体情况分析原因
	3>正数—该中断号可用
*/
extern unsigned int probe_irq_on(void);	/* returns 0 on failure */
extern int probe_irq_off(unsigned int); /* returns 0 or negative on failure */

#endif
